<template>
    <div class="generic-type-instantiate">
        <div class="flex-1 nowrap cursor-default me-2"
            :title="joinCaption(false)"
            v-if="types.length">
            {{ joinCaption(true) }}
        </div>
        <div class="text-placeholder me-2" v-else>&lt;无&gt;</div>
        <tnxel-button class="px-2"
            type="primary"
            text
            bg
            icon="MoreFilled"
            @click="toUpdate"
        />
        <tnxel-dialog v-model="dialog.visible"
            :buttons="dialog.buttons"
            :title="title"
            :width="600"
        >
            <tnxel-edit-table ref="table"
                :data="types"
                :addable="false"
                :removable="false"
            >
                <el-table-column label="序号" type="index" align="center" width="60"/>
                <el-table-column label="声明泛型" prop="parameter">
                    <template #default="{row}">
                        <span class="w-100 px-2 cursor-default"
                            :title="GenerateUtil.generateGenericTypeCaption(app, row.limit)">
                            {{ GenerateUtil.generateGenericTypeCaption(app, row.limit, true) }}
                        </span>
                    </template>
                </el-table-column>
                <el-table-column label="具化类型" prop="typeId">
                    <template #default="{row}">
                        <LogicTypeSelect v-model="row.typeId"
                            :app="app"
                            :kind="TypeKind.GENERIC"
                            :declared-generic-types="declared"
                            @change="onTypeIdChange(row)"
                        />
                    </template>
                </el-table-column>
                <el-table-column label="数组" prop="arrayed" align="center" width="60">
                    <template #default="{ row }">
                        <ArrayedSelect v-model="row.arrayed" :property="row"/>
                    </template>
                </el-table-column>
            </tnxel-edit-table>
        </tnxel-dialog>
    </div>
</template>

<script>
import tnx from '@/tnx.js';
import ModelingApp from '@/logic/modeling/entity/ModelingApp.js';
import TypeKind from '@/logic/modeling/enums/TypeKind.js';
import GenericType from '@/logic/modeling/entity/GenericType.js';
import GenerateUtil from '@/logic/modeling/util/generate.js';

import ArrayedSelect from './ArrayedSelect.vue';
import LogicTypeSelect from './LogicTypeSelect.vue';

export default {
    name: 'GenericTypeInstantiate',
    components: {ArrayedSelect, LogicTypeSelect},
    props: {
        modelValue: {
            type: Array,
            default: () => [],
        },
        app: {
            type: ModelingApp,
            required: true,
        },
        limited: { // 限定泛型集，来自父类
            type: Array,
            required: true,
        },
        declared: { // 声明泛型集，来自自身
            type: Array,
            required: true,
        },
        title: {
            type: String,
            default: '泛型具化',
        },
    },
    data() {
        return {
            TypeKind, GenerateUtil,
            types: [],
            extendedItems: [{
                text: 'extends',
                value: 'true',
            }, {
                text: 'super',
                value: 'false',
            }],
            dialog: {
                visible: false,
                buttons: tnx.getDefaultDialogButtons('confirm', this.toSubmitUpdate),
            },
        };
    },
    watch: {
        modelValue() {
            this.initTypes();
        },
    },
    mounted() {
        this.initTypes();
    },
    methods: {
        joinCaption(simple) {
            return GenerateUtil.generateGenericTypesCaption(this.app, this.modelValue, simple);
        },
        initTypes() {
            this.types.length = 0;
            for (let i = 0; i < this.limited.length; i++) {
                let limitedType = this.limited[i];
                let modelType = this.modelValue[i];
                if (!modelType) {
                    modelType = limitedType.clone();
                    this.modelValue[i] = modelType;
                }
                let type = modelType.clone();
                type.limit = limitedType;
                if (type.parameter && !type.typeId) {
                    type.typeId = LogicTypeSelect.DECLARED_GENERIC_TYPE_ID_PREFIX + type.parameter;
                    delete type.parameter;
                }
                this.types.push(type);
            }
        },
        toUpdate() {
            this.dialog.visible = true;
        },
        assignName(targetType, sourceType) {
            targetType.packageName = sourceType.packageName || '';
            targetType.simpleName = sourceType.simpleName || '';
            targetType.name = sourceType.getName ? sourceType.getName() : (sourceType.name || '');
        },
        onTypeIdChange(row) {
            if (row.typeId) {
                let refClass = this.app.findClass(row.typeId, true, true);
                if (refClass) {
                    this.assignName(row, refClass);
                    if (row.extended === null || row.extended === '') {
                        row.extended = 'true';
                    }
                    return;
                }
            }
            this.assignName(row, {});
            row.extended = null;
        },
        toSubmitUpdate(yes) {
            if (yes) {
                if (!this.$refs.table.validateTable()) {
                    return false;
                }
                let instantiatedGenericTypes = [];
                for (let type of this.types) {
                    let model = {
                        arrayed: type.arrayed,
                    };
                    if (type.typeId.startsWith(LogicTypeSelect.DECLARED_GENERIC_TYPE_ID_PREFIX)) {
                        model.parameter = type.typeId.substring(LogicTypeSelect.DECLARED_GENERIC_TYPE_ID_PREFIX.length);
                    } else {
                        model.typeId = type.typeId;
                    }
                    let instantiatedGenericType = GenericType.of(model);
                    instantiatedGenericTypes.push(instantiatedGenericType);
                }
                this.$emit('update:modelValue', instantiatedGenericTypes);
            }
        },
    }
}
</script>

<style lang="scss">
.generic-type-instantiate {
    display: flex;
    align-items: center;
}
</style>
